#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/numa.h>
#include <linux/string.h>

#define INIT_MEMBLOCK_REGIONS 16
#define INIT_PHYSMEM_REGIONS 4

#ifndef INIT_MEMBLOCK_RESERVED_REGIONS
#define INIT_MEMBLOCK_RESERVED_REGIONS INIT_MEMBLOCK_REGIONS
#endif

#ifndef INIT_MEMBLOCK_MEMORY_REGIONS
#define INIT_MEMBLOCK_MEMORY_REGIONS INIT_MEMBLOCK_REGIONS
#endif

#define MAX_NUMNODES 4

static struct memblock_region _memory_init_regions[INIT_MEMBLOCK_MEMORY_REGIONS];
static struct memblock_region _reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS];

struct memblock memblock = {
    .memory.regions = _memory_init_regions,
    .memory.max = INIT_MEMBLOCK_MEMORY_REGIONS,
    .memory.name = "memory",

    .reserved.regions = _reserved_init_regions,
    .reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS,
    .reserved.name = "reserved",

    .bottom_up = false,
};

#include "_mb_/find.c"
#include "_mb_/add_range.c"
#include "_mb_/remove_range.c"
#include "_mb_/alloc.c"

int memblock_add(phys_addr_t base, phys_addr_t size)
{
    phys_addr_t end = base + size - 1;

    return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0);
}

int memblock_phys_free(phys_addr_t base, phys_addr_t size)
{
    phys_addr_t end = base + size - 1;

    return memblock_remove_range(&memblock.reserved, base, size);
}

int memblock_free(void *ptr, size_t size)
{
    if (ptr)
        memblock_phys_free(__pa(ptr), size);
}

void *memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align,
                             phys_addr_t min_addr, phys_addr_t max_addr,
                             int nid)
{
    void *ptr;

    ptr = memblock_alloc_internal(size, align,
                                  min_addr, max_addr, nid, false);

    if (ptr)
        memset(ptr, 0, size);

    return ptr;
}

void *memblock_alloc(phys_addr_t size, phys_addr_t align)
{
    return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT,
                                  MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE);
}

phys_addr_t memblock_alloc_range_nid(phys_addr_t size,
                                     phys_addr_t align, phys_addr_t start,
                                     phys_addr_t end, int nid,
                                     bool exact_nid)
{
    enum memblock_flags flags = MEMBLOCK_NONE;
    phys_addr_t found;

    found = memblock_find_in_range_node(size, align, start, end, nid,
                                        flags);

    return found;
}

int memblock_reserve(phys_addr_t base, phys_addr_t size)
{
    phys_addr_t end = base + size - 1;

    return memblock_add_range(&memblock.reserved, base, size, MAX_NUMNODES, 0);
}

phys_addr_t memblock_start_of_DRAM(void)
{
    return memblock.memory.regions[0].base;
}

int memblock_get_region_node(const struct memblock_region *r)
{
	return r->nid;
}
